package cn.zlp.hrm.service.impl;

import cn.zlp.hrm.domain.KillCourse;
import cn.zlp.hrm.dto.KillDto;
import cn.zlp.hrm.exception.ValidUtils;
import cn.zlp.hrm.mapper.KillCourseMapper;
import cn.zlp.hrm.service.IKillCourseService;
import cn.zlp.hrm.vo.PreCreateOrderData;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.apache.commons.lang.time.DateUtils;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zlp
 * @since 2022-01-20
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private KillCourseMapper killCourseMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void publishKillCourse2Redis() {
        // 1查询所有满足条件课程,状态为待发布，且开始时间在两天之内
        Date date = new Date();
        Date addDays = DateUtils.addDays(date, 2);
        Wrapper<KillCourse> wrapper = new EntityWrapper<KillCourse>().eq("kill_status", 0)
                .and()
                .between("start_time", date, addDays);
        List<KillCourse> killCourses = killCourseMapper.selectList(wrapper);
        killCourses.forEach(killCourse -> {
            // 2.设置秒杀验证码
            killCourse.setKillCode(UUID.randomUUID().toString());
            // 3.课程信息redis缓存预热 K k json,如果存在了就不添加
            Boolean success = redisTemplate.opsForHash().putIfAbsent("kill_course", killCourse.getId().toString(), killCourse);
            // 4.为每个课程添加预热库存
            if (success){
                RSemaphore semaphore = redissonClient.getSemaphore("kill_course_" + killCourse.getId());
                boolean success1 = semaphore.trySetPermits(killCourse.getKillCount());
                if (success1){
                    // 5.修改数据库状态为已发布
                    killCourse.setKillStatus(KillCourse.KILL_STATUS_PUBLISHED);
                    killCourseMapper.updateById(killCourse);
                } else {
                    //失败了要把课程信息缓存信息回滚
                    redisTemplate.opsForHash().delete("kill_course",killCourse.getId().toString());
                }
            }

        });
    }

    /**
     *  从redis获取
     * 1）正在秒杀或者将要秒杀  当前时间大于结束时间
     * 2）是列表页使用的（不需要返回秒杀码）
     * @return
     */
    @Override
    public List<KillCourse> listFront() {
        //1 从redis中查询所有发布的课程
        List<KillCourse> killCourseList =
                redisTemplate.opsForHash().values("kill_course");
        if (killCourseList==null) {
            return null;
        }
        killCourseList.stream().filter(killCourse -> {
            // 1）正在秒杀或者将要秒杀,当前时间大于结束时间
            if (!new Date().after(killCourse.getEndTime())){
                // 2）是列表页使用的（不需要返回秒杀码）
                killCourse.setKillCode("");
                return true;
            }
            return false;
        }).sorted(new Comparator<KillCourse>() { // 按照开始时间升序
            @Override
            public int compare(KillCourse o1, KillCourse o2) {
                return o1.getStartTime().getTime()-o2.getStartTime().getTime()>0? 1: -1;
            }
        }).collect(Collectors.toList());
        return killCourseList;
    }

    @Override
    public KillCourse getFrontById(Long id) {
        ValidUtils.assertNotNull(id,"非法请求");
        BoundHashOperations<Object,Object,KillCourse> operations = redisTemplate
                .boundHashOps("kill_course");
        KillCourse killCourse = operations.get(id.toString());
        if (!new Date().after(killCourse.getStartTime())) { //还没有开始,所以不返回killCode秒杀码
            killCourse.setKillCode("");
        }
        return killCourse;
    }

    @Override
    public void kill(KillDto killDto) {
        //1 校验
        //1.1 null校验    时间  是否秒杀过 码
        ValidUtils.assertNotNull(killDto,"非法请求");
        Long killCourseId = killDto.getKillCourseId();
        String killCode = killDto.getKillCode();
        ValidUtils.assertNotNull(killCourseId,"非法请求");
        ValidUtils.assertNotNull(killCode,"非法请求");
        //1.2 校验秒杀码
        BoundHashOperations<Object,Object,KillCourse> operations = redisTemplate
                .boundHashOps("kill_course");
        KillCourse killCourse = operations.get(killCourseId.toString());
        ValidUtils.assertNotNull(killCourse,"没有相关的课程可进行秒杀");
        ValidUtils.assertTrue(killCode.equals(killCourse.getKillCode()),"非法请求！");
        //1.3 校验时间
        Date now = new Date();
        if (now.before(killCourse.getStartTime())|| now.after(killCourse.getEndTime())) {
            ValidUtils.assertNotNull(null, "没有相关的课程可进行秒杀!");
        }

        //获取用户信息 集成授权服务资源服务，通过UserContextInfoHolder获取用户信息，现在模拟一些就ol
        Long userId = 13L;
        String hashKey = "killCoursePreCreateOrder"+userId;
        String hashKkey =killCourse.getSessionNumber()+killCourseId;
        //1.5 是否已经秒杀过
//        Object preCreateOrder = redisTemplate.opsForHash().get(hashKey, hashKkey);
//        ValidUtils.assertNull(preCreateOrder,"您已经秒杀过了，不能重复秒杀！");

        //2 减库存
        RSemaphore semaphore = redissonClient
                .getSemaphore("kill_course_" + killCourse.getId());
        boolean success = semaphore.tryAcquire(1);
        ValidUtils.assertTrue(success,"你是出的吗？手速优点慢！");
        //3 如果秒杀成功，预创订单 orderSn:userId+场次号+courseId  hash(willOrder_userId，场次号+courseId)
        PreCreateOrderData data = new PreCreateOrderData();
        data.setKillCousreId(killCourseId);
        data.setKillCousreName(killCourse.getCourseName());
        String preCreateOrderSn = hashKey+"_"+hashKkey;
        data.setPreCreateOrderSn(preCreateOrderSn);
        data.setPrice(killCourse.getKillPrice());
        data.setSessionNumber(killCourse.getSessionNumber());
        data.setUserId(userId);
        data.setUsername("18244444444");
        redisTemplate.opsForHash().put(hashKey,hashKkey,data);
        //4 无论成功与否添加秒杀历史  放到消息队列，慢慢处理 @TODO
    }
    
}
